#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtGui>
#include <QDesktopWidget>
#include <QLabel>
#include <QComboBox>
#include <QToolBar>
#include <QHostAddress>
#include <QNetworkInterface>
#include <QPageSetupDialog>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QProcess>
#include <QMessageBox>
#include <QListWidget>
#include <QFileDialog>
#include <QFileInfo>
#include <iostream>
using namespace std;

MainWindow *MainWindow::instance = NULL;

MainWindow::MainWindow(): ui(new Ui::MainWindow)
{
	ui->setupUi(this);

	welcome = NULL;
	newFile = NULL;
	prefs = NULL;
	about = NULL;
	stats = NULL;
	manual = NULL;
	find = NULL;
	prefs = Preferences::getInstance();
	goToLine = NULL;

	documentMenuList = new QActionGroup(this);
	documentMenuList->setExclusive(false);

	programmingLanguages = new QActionGroup(this);
	programmingLanguages->addAction(ui->actionNone);
	programmingLanguages->addAction(ui->actionC_CPlusPlus);
	programmingLanguages->addAction(ui->actionPython);
	programmingLanguages->addAction(ui->actionSh_Bash);
	programmingLanguages->addAction(ui->actionJava);
	programmingLanguages->addAction(ui->actionHTML);
	programmingLanguages->addAction(ui->actionJavaScript);
	programmingLanguages->addAction(ui->actionCSharp);
	programmingLanguages->addAction(ui->actionFortran);

	setIcons();
	setGeometry();

	connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(quitSession()));
	connect(ui->actionNew, SIGNAL(triggered()), this, SLOT(createNewFile()));
	connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(openFiles()));
	connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(saveFile()));
	connect(ui->actionSave_As, SIGNAL(triggered()), this, SLOT(saveFileAs()));
	connect(ui->actionClose_File, SIGNAL(triggered()), this, SLOT(closeFile()));
	connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), SLOT(closeFileTab(int)));
	connect(ui->actionPage_Setup, SIGNAL(triggered()), this, SLOT(pageSetup()));
	connect(ui->actionPrint, SIGNAL(triggered()), this, SLOT(print()));
	connect(ui->actionPrint_Preview, SIGNAL(triggered()), this, SLOT(printPreview()));
	connect(ui->actionPreferences, SIGNAL(triggered()), this, SLOT(showPrefs()));
	connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAbout()));
	connect(ui->actionStatistics, SIGNAL(triggered()), this, SLOT(showStats()));
	connect(ui->actionManual, SIGNAL(triggered()), this, SLOT(showManual()));
	connect(ui->actionFind, SIGNAL(toggled(bool)), this, SLOT(showFind(bool)));
	connect(ui->actionReplace, SIGNAL(toggled(bool)), this, SLOT(showReplace(bool)));
	connect(ui->actionTool_Bar, SIGNAL(toggled(bool)), ui->toolBar, SLOT(setVisible(bool)));
	connect(ui->actionClose_All, SIGNAL(triggered()), this, SLOT(closeAllFiles()));
	connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentEditorChanged(int)));
	connect(ui->actionGo_to_Line, SIGNAL(triggered()), this, SLOT(showGoToLine()));

	connect(ui->actionChat, SIGNAL(triggered(bool)), ui->dockWidget_chat, SLOT(setVisible(bool)));
	connect(ui->actionUsers, SIGNAL(triggered(bool)), ui->dockWidget_users, SLOT(setVisible(bool)));
	connect(ui->dockWidget_chat->toggleViewAction(), SIGNAL(toggled(bool)), ui->actionChat, SLOT(setChecked(bool))); //connecting an action to an action!
	connect(ui->dockWidget_users->toggleViewAction(), SIGNAL(toggled(bool)), ui->actionUsers, SLOT(setChecked(bool)));

	connect(ui->lineEdit_chat, SIGNAL(returnPressed()), this, SLOT(chatSend()));
	connect(ui->actionJoin_Session, SIGNAL(triggered()), this, SLOT(joinSession()));
	connect(ui->actionCreate_Session, SIGNAL(triggered()), this, SLOT(createSession()));
	connect(ui->actionLeave_Session, SIGNAL(triggered()), this, SLOT(leaveSession()));
	connect(ui->actionSave_Session, SIGNAL(triggered()), this, SLOT(saveSession()));                        //Connect up save session button
	connect(ui->actionOpen_Session, SIGNAL(triggered()), this, SLOT(loadSession()));
	connect(ui->listWidget_users, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showPrefs()));	//clicking on the user list shows prefs

	//create the session ...and here we go...
	session = new Session();
}

MainWindow::~MainWindow()
{
	delete ui;
	delete documentMenuList;
	delete programmingLanguages;

	ui = NULL;
}

void MainWindow::moveEvent(QMoveEvent *event)
{
	QMainWindow::moveEvent(event);

	emit moved();
}

/**
  * Overridden close event, save window geometry on close.
  */
void MainWindow::closeEvent(QCloseEvent *event)
{
	prefs->setValue(Preferences::SETTING_SAVE_WINDOW_GEOMETRY, saveGeometry());

	cleanUp();

	QWidget::closeEvent(event);
}

/**
  * Set toolbar and menu icons using the freedesktop.org standard. A fallback is
  * provided for the systems that don't respect this standard (Windows).
  *
  * We will need to create SOME icons ourselves as the SP_EXAMPLE fallback icons are
  * very very limited.
  */
void MainWindow::setIcons()
{
	ui->actionCreate_Session->setIcon(QIcon::fromTheme("network-server", QIcon(":mainToolbarIcons/images/mainToolbar/create.png")));
	ui->actionJoin_Session->setIcon(QIcon::fromTheme("network-transmit-receive", QIcon(":mainToolbarIcons/images/mainToolbar/join.png")));
	ui->actionNew->setIcon(QIcon::fromTheme("document-new", QIcon(":mainToolbarIcons/images/mainToolbar/new.png")));
	ui->actionOpen->setIcon(QIcon::fromTheme("document-open", QIcon(":mainToolbarIcons/images/mainToolbar/open.png")));
	ui->actionSave->setIcon(QIcon::fromTheme("document-save", QIcon(":mainToolbarIcons/images/mainToolbar/save.png")));
	ui->actionSave_As->setIcon(QIcon::fromTheme("document-save-as"));
	ui->actionPrint->setIcon(QIcon::fromTheme("document-print", QIcon(":mainToolbarIcons/images/mainToolbar/print.png")));
	ui->actionPrint_Preview->setIcon(QIcon::fromTheme("document-print-preview"));
	ui->actionUndo->setIcon(QIcon::fromTheme("edit-undo", QIcon(":mainToolbarIcons/images/mainToolbar/undo.png")));
	ui->actionRedo->setIcon(QIcon::fromTheme("edit-redo", QIcon(":mainToolbarIcons/images/mainToolbar/redo.png")));
	ui->actionCut->setIcon(QIcon::fromTheme("edit-cut", QIcon(":mainToolbarIcons/images/mainToolbar/cut.png")));
	ui->actionCopy->setIcon(QIcon::fromTheme("edit-copy", QIcon(":mainToolbarIcons/images/mainToolbar/copy.png")));
	ui->actionPaste->setIcon(QIcon::fromTheme("edit-paste", QIcon(":mainToolbarIcons/images/mainToolbar/paste.png")));
	ui->actionDelete->setIcon(QIcon::fromTheme("edit-delete"));
	ui->actionSelect_All->setIcon(QIcon::fromTheme("edit-select-all"));
	ui->actionUnindent->setIcon(QIcon::fromTheme("format-indent-less"));
	ui->actionIndent->setIcon(QIcon::fromTheme("format-indent-more"));
	ui->actionPreferences->setIcon(QIcon::fromTheme("preferences-system"));
	ui->actionFind->setIcon(QIcon::fromTheme("edit-find", QIcon(":mainToolbarIcons/images/mainToolbar/find.png")));
	ui->actionReplace->setIcon(QIcon::fromTheme("edit-find-replace", QIcon(":mainToolbarIcons/images/mainToolbar/replace.png")));
	ui->actionManual->setIcon(QIcon::fromTheme("help-contents"));
	ui->actionAbout->setIcon(QIcon::fromTheme("help-about"));
	// Etc etc.
}

/**
  * Method sets window geometry based on user settings.
  */
void MainWindow::setGeometry()
{
	// Check if user has saved settings.
	if (prefs->getValue(Preferences::SETTING_SAVE_WINDOW_GEOMETRY,
				QVariant::fromValue(true).toBool()).toBool())
		restoreGeometry(prefs->getValue(Preferences::SETTING_SAVE_WINDOW_GEOMETRY).toByteArray());

	// Nope, centre on screen instead.
	else
	{
		QDesktopWidget *desktop = QApplication::desktop();

		// Calculate where to place application.
		int x = (desktop->width() - width()) / 2;
		int y = (desktop->height() - height()) / 2;
		y -= 50;

		move(x, y);

		desktop = NULL;
	}
}

/**
  *	Used by the session classes (esp DocumentManager) to add a new Editor to the mainwindow
  * ALL additions of editors SHOULD go through this function
  */
void MainWindow::addEditorToTab(Editor *newEditor)
{
	hideWelcome();

	newEditor->setParent(ui->tabWidget);	//fix up parenting

	QString tabName = newEditor->getTabName();		//fix up naming

	if (tabName.isEmpty())
	{
	    tabName = getNextTabName();
	    newEditor->setTabName(tabName);
	}

	int tabNumber = ui->tabWidget->addTab(newEditor, tabName);
	ui->tabWidget->setCurrentWidget(newEditor);

	if (newEditor->getType() == Editor::TEXT)
	{
		TextEditor *editor = qobject_cast<TextEditor*>(newEditor);

		connect(ui->actionLine_Numbers, SIGNAL(toggled(bool)), editor, SLOT(setLineNumbers(bool)));
		connect(programmingLanguages, SIGNAL(triggered(QAction*)), editor, SLOT(changeProgrammingLanguage(QAction *)));
	}

	//connect the document highlighter to the highlighter ui button
	connect(ui->actionHighlight, SIGNAL(toggled(bool)), newEditor->getUserHighlighter(), SLOT(toggleHighlighting(bool)));

	// Connect all of the things from the edit menu
	connect(ui->actionIndent, SIGNAL(triggered()), newEditor, SLOT(indent()));
	connect(ui->actionUnindent, SIGNAL(triggered()), newEditor, SLOT(unindent()));
	connect(ui->actionSelect_All, SIGNAL(triggered()), newEditor, SLOT(selectAll()));
	connect(ui->actionSelect_None, SIGNAL(triggered()), newEditor, SLOT(selectNone()));
	connect(ui->actionCopy, SIGNAL(triggered()), newEditor, SLOT(copy()));
	connect(ui->actionCut, SIGNAL(triggered()), newEditor, SLOT(cut()));
	connect(ui->actionPaste, SIGNAL(triggered()), newEditor, SLOT(paste()));
	connect(ui->actionDelete, SIGNAL(triggered()), newEditor, SLOT(deleteSelection()));
	connect(ui->actionUndo, SIGNAL(triggered()), newEditor, SLOT(undo()));
	connect(ui->actionRedo, SIGNAL(triggered()), newEditor, SLOT(redo()));

	//if highlighting is already on in the ui, turn on the document highlighter
	if (ui->actionHighlight->isChecked())
		newEditor->getUserHighlighter()->toggleHighlighting(true);

	// Focus on editor.
	newEditor->setFocus();
	showEditorUiComponents();

	//Add the editor to the document list.
	QAction *newDocAction = new QAction(tabName, documentMenuList);
	newDocAction->setCheckable(true);
	newDocAction->setChecked(true);

	if (ui->menuDocuments->actions().back() == ui->actionDocListEmpty)
		ui->menuDocuments->removeAction(ui->actionDocListEmpty);

	ui->menuDocuments->addAction(newDocAction);

	connect(newDocAction, SIGNAL(triggered(bool)), newEditor, SLOT(hideOrRestoreDocument(bool)));

	currentEditorChanged(tabNumber);
}

void MainWindow::hideEditorTab(Editor *ed)
{
	int index = ui->tabWidget->indexOf(ed);

	//if the editor is already in the tab widget remove it
	if (index != -1)
		ui->tabWidget->removeTab(index);
}

void MainWindow::restoreEditorTab(Editor *ed)
{
	ui->tabWidget->insertTab(ed->getDocId(),ed, ed->getTabName());
	ui->tabWidget->setCurrentWidget(ed);
}

/**
  * Displays a message box. Used by the poor and underpriveleged threads
  * that cannot act directly on the UI. Must be called via signals/slots
  */
void MainWindow::showMessageBox(int ico, QString message)
{
	QMessageBox box;
	box.setIcon((QMessageBox::Icon)ico);
	box.setText(message);
	box.exec();
}

Editor* MainWindow::getCurrentEditor()
{
	return qobject_cast<Editor*>(ui->tabWidget->currentWidget());

}

void MainWindow::createSession()
{
	CreateSession *createDialog = new CreateSession(this);

	if (createDialog->exec() == QDialog::Accepted)
	{
		hideWelcome();

		QString sessionName = createDialog->getSessionName();
		int port = createDialog->getPort();

		User usr = session->getUserManager()->getLocalUser();
		usr.setName(createDialog->getUserName());
		usr.setColour(createDialog->getUserColour());
		usr.setOnlineStatus(true);
		session->getUserManager()->setLocalUser(usr);

		ui->actionCreate_Session->setEnabled(false);
		ui->actionJoin_Session->setEnabled(false);
		ui->actionJoin_Session->setVisible(false);
		ui->actionLeave_Session->setVisible(true);
		ui->actionLeave_Session->setEnabled(true);
		ui->dockWidget_users->show();
		ui->dockWidget_chat->show();
		ui->actionChat->setEnabled(true);
		ui->actionUsers->setEnabled(true);
		ui->actionHighlight->setEnabled(true);
		ui->actionSave_Session->setEnabled(true);
		ui->actionOpen_Session->setEnabled(false);

		session->runAsServer(sessionName, port);

		if (createDialog->getDocumentType() == NewFile::DOCUMENT_TEXT)
			session->getDocumentManager()->createDocument(Editor::TEXT);

		else if (createDialog->getDocumentType() == NewFile::DOCUMENT_FORMATTED)
			session->getDocumentManager()->createDocument(Editor::FORMATTED);

		QString msg = " has created the session.";
		session->getChatManager()->sendSys(msg);
	}

	delete createDialog;
}

void MainWindow::joinSession()
{
	JoinSession *joinDialog = new JoinSession(this);

	if (joinDialog->exec() == QDialog::Accepted)
	{
		QPair<QString, quint16> data = joinDialog->getSelectedConnectionData();

		User usr = session->getUserManager()->getLocalUser();
		usr.setName(joinDialog->getUserName());
		usr.setColour(joinDialog->getUserColour());
		usr.setOnlineStatus(true);
		session->getUserManager()->setLocalUser(usr);

		joinSession(data.first, (int)data.second);
	}

	delete joinDialog;
}

/**
  * Connects to the session, if there are already tabs open, we will fork
  * another instance of the program and join in that instance instead
  */
void MainWindow::joinSession(QString addy, int port)
{
	if (ui->tabWidget->count() == 0)
	{
		if (!session->connectToServer(addy,port))
		{
			QMessageBox(QMessageBox::Critical, "Error", "The server is not responding to your request to join",
						QMessageBox::Ok, this).exec();
		}
		else
		{
			hideWelcome();
			ui->actionCreate_Session->setEnabled(false);
			ui->actionJoin_Session->setEnabled(false);
			ui->actionJoin_Session->setVisible(false);
			ui->actionLeave_Session->setVisible(true);
			ui->actionLeave_Session->setEnabled(true);
			ui->dockWidget_users->show();
			ui->dockWidget_chat->show();
			ui->actionChat->setEnabled(true);
			ui->actionUsers->setEnabled(true);
			ui->actionSave_Session->setEnabled(true);
			ui->actionOpen_Session->setEnabled(false);

			QString msg = " has joined the session.";
			session->getChatManager()->sendSys(msg);
		}
	}
	else
	{
		//fork another WeaveEdit
		QStringList args;
		args << addy << QString::number(port);

		QProcess::startDetached(execName, args);
	}
}

void MainWindow::cleanUp()
{
	//Ask to Save files if changes have been made

	//send out that you are leaving
	QString msg = " has left the session.";
	session->getChatManager()->sendSys(msg);

	User usr = session->getUserManager()->getLocalUser();
	usr.setOnlineStatus(false);
	session->getUserManager()->setLocalUser(usr);

	//TODO: Delete session and whatnot - properly.
	//MAKE SURE BONJOUR SERVER DIES!!!!!! AND THE REST OF THE SERVER
}

void MainWindow::leaveSession()
{
	cleanUp();

	if (session->getDocumentManager()->getDocList().isEmpty())
		showWelcome(true);

	ui->dockWidget_chat->hide();
	ui->dockWidget_users->hide();
	ui->actionChat->setEnabled(false);
	ui->actionUsers->setEnabled(false);
	ui->actionCreate_Session->setEnabled(true);
	ui->actionOpen_Session->setEnabled(true);
	ui->actionSave_Session->setEnabled(false);
	ui->actionLeave_Session->setEnabled(false);
	ui->actionLeave_Session->setVisible(false);
	ui->actionJoin_Session->setEnabled(true);
	ui->actionJoin_Session->setVisible(true);
}

void MainWindow::quitSession()
{
	cleanUp();
	close();
}

/**
  * Show welcome screen with quick access to recent documents and help.
  * @param show Whether welcome screen is shown or hidden.
  * @see hideWelcome()
  */
void MainWindow::showWelcome(const bool show)
{
	if (!show)
	{
		hideWelcome();

		return;
	}

	if (!welcome)
	{
		welcome = WelcomeScreen::getInstance();

		connect(welcome, SIGNAL(recentSessionSelected(QString)), this, SLOT(loadSession(QString)));
		connect(welcome, SIGNAL(recentFileSelected(QString)), this, SLOT(openFile(QString)));

		QPushButton *createButton = welcome->findChild<QPushButton *>("pushButton_create");
		QPushButton *joinButton = welcome->findChild<QPushButton *>("pushButton_join");

		if (createButton)
			connect(createButton, SIGNAL(clicked()), this, SLOT(createSession()));

		if (joinButton)
			connect(joinButton, SIGNAL(clicked()), this, SLOT(joinSession()));
	}

	ui->centralwidget->layout()->addWidget(welcome);
	ui->centralwidget->layout()->setMargin(0);
	ui->tabWidget->hide();

	//hide and disable the toolbars
	ui->toolBar->hide();
	ui->dockWidget_chat->hide();
	ui->dockWidget_users->hide();
	ui->actionTool_Bar->setEnabled(false);
	ui->actionChat->setEnabled(false);
	ui->actionUsers->setEnabled(false);
	ui->actionHighlighting_Mode->setEnabled(false);
	ui->actionHighlight->setEnabled(false);
	ui->actionCreate_Session->setEnabled(true);
	ui->actionOpen_Session->setEnabled(true);
	ui->actionSave_Session->setEnabled(false);

	welcome->show();

	showEditorUiComponents(false);
}

/**
  * Hide welcome screen.
  */
void MainWindow::hideWelcome()
{
	welcome->hide();

	ui->toolBar->show();
	ui->tabWidget->show();
	ui->actionTool_Bar->setEnabled(true);

	if (session->isServer() || session->isConnectedToServer())
	{
		ui->dockWidget_users->show();
		ui->dockWidget_chat->show();
		ui->actionChat->setEnabled(true);
		ui->actionUsers->setEnabled(true);
		ui->actionHighlight->setEnabled(true);
	}

	ui->centralwidget->layout()->setMargin(9);

	if (session->getDocumentManager()->numEditors() > 0)
		showEditorUiComponents();
}

/**
  * Enables/Disables UI components that need an editor to function, such as printing buttons.
  */
void MainWindow::showEditorUiComponents(const bool show)
{
	ui->actionSave->setEnabled(show);
	ui->actionSave_As->setEnabled(show);
	ui->actionSave_All->setEnabled(show);
	ui->actionClose_File->setEnabled(show);
	ui->actionClose_All->setEnabled(show);
	ui->actionPage_Setup->setEnabled(show);
	ui->actionPrint->setEnabled(show);
	ui->actionPrint_Preview->setEnabled(show);
	ui->actionUndo->setEnabled(show);
	ui->actionRedo->setEnabled(show);
	ui->actionCut->setEnabled(show);
	ui->actionCopy->setEnabled(show);
	ui->actionPaste->setEnabled(show);
	ui->actionDelete->setEnabled(show);
	ui->actionSelect_All->setEnabled(show);
	ui->actionSelect_None->setEnabled(show);
	ui->actionIndent->setEnabled(show);
	ui->actionUnindent->setEnabled(show);
	ui->actionFind->setEnabled(show);
	ui->actionReplace->setEnabled(show);
	ui->actionGo_to_Line->setEnabled(show);
	ui->actionStatistics->setEnabled(show);
	ui->actionLine_Numbers->setEnabled(show);
	ui->menuProgramming_Language->setEnabled(show);
}

/**
  * Create a new file using a dialog to select type.
  */
void MainWindow::createNewFile()
{
	if (!newFile)
		newFile = new NewFile(this);

	if (newFile->exec() == QDialog::Accepted)
	{
		// Get type of initial document. This is for text.
		if (newFile->getDocumentType() == NewFile::DOCUMENT_TEXT)
			session->getDocumentManager()->createDocument(Editor::TEXT);

		// Formatted document.
		else
			session->getDocumentManager()->createDocument(Editor::FORMATTED);
	}
}

/**
  * Get files to open through a dialog. Users can select as many files as desired.
  */
void MainWindow::openFiles()
{
	QStringList fileNames = QFileDialog::getOpenFileNames(this, "Open Files", QString(),
								  QString(SUPPORTED_FORMATTED_FILE_FILTER) +
								  SUPPORTED_TEXT_FILE_FILTER);

	foreach (QString fileName, fileNames)
		openFile(fileName);
}

/**
  * Open a file given the location. Creates the most appropriate editor (text or formatted) depending
  * on the file extension.
  */
void MainWindow::openFile(const QString fileName)
{
	QFile file(fileName);

	file.open(QFile::ReadOnly);

	if (!file.exists())
	{
		QMessageBox::StandardButton response = QMessageBox::information(this,
				"Couldn't Open File", "File could not "
				"be opened as it does not exist\n\nDo you want "
				"to create the file now?", QMessageBox::Yes |
				QMessageBox::No);

		if (response == QMessageBox::Yes)
			createNewFile();

		else
			return;
	}

	else
		session->getDocumentManager()->loadDocument(fileName);

	hideWelcome();
}

void MainWindow::saveFile(const bool saveAs)
{
	Editor *editor = qobject_cast<Editor *>(ui->tabWidget->currentWidget());

	if (editor->getType() == Editor::TEXT)
	{
		if (saveAs || editor->getFileName().isEmpty())
			editor->setFileName(getSaveFileName(TEXT_FILE));
	}

	else if (editor->getType() == Editor::FORMATTED)
	{
		if (saveAs || editor->getFileName().isEmpty())
			editor->setFileName(getSaveFileName(FORMATTED_FILE));
	}

	editor->saveFile();

	ui->actionSave->setEnabled(false);

	WelcomeScreen::getInstance()->addRecentFile(editor->getFileName(), editor->getType());
}

void MainWindow::saveFileAs()
{
	saveFile(true);
}

QString MainWindow::getSaveFileName(FILE_TYPE type)
{
	QString filter = QString();

	if (type == TEXT_FILE)
		filter = QFileDialog::getSaveFileName(this, "Save File As",
							QString(), SUPPORTED_TEXT_FILE_FILTER);


	else if (type == FORMATTED_FILE)
		filter = QFileDialog::getSaveFileName(this, "Save File As",
							QString(), SUPPORTED_FORMATTED_FILE_FILTER);

	return filter;
}

/** Close the file opened in the current tab.*/
void MainWindow::closeFile()
{
	if (!session->isConnectedToServer())
	{
		closeFileTab(ui->tabWidget->currentIndex());
	}
}

/** Close file at tab index index. */
void MainWindow::closeFileTab(const int index)
{
	// TODO: stuff like making sure file is saved, close file properly etc...

	// Remove tab.
	if (!session->isConnectedToServer())
	{
		Editor *editor = qobject_cast<Editor *>(ui->tabWidget->widget(index));	//get the editor object

		QAction *edAction = documentMenuList->actions().at(index);
		documentMenuList->removeAction(edAction);	//remove from the document list
		ui->menuDocuments->removeAction(edAction);

		if (documentMenuList->actions().isEmpty())
			ui->menuDocuments->addAction(ui->actionDocListEmpty);	//add "(empty)" to list if it is empty

		ui->tabWidget->removeTab(index);
		session->getDocumentManager()->closeDocument(editor);

		showEditorUiComponents(false);
	}
	else
	{
		Editor *editor = qobject_cast<Editor *>(ui->tabWidget->widget(index));
		documentMenuList->actions().at(index)->setChecked(false);
		editor->hideOrRestoreDocument(false);
	}
}

void MainWindow::closeAllFiles()
{
	if (!session->isConnectedToServer())
	{
		// Close in reverse order because removing tab 0 will move tab 1 to tab 0.
		for (int i = ui->tabWidget->count() - 1; i >= 0; i--)
			closeFileTab(i);
	}
}

void MainWindow::currentEditorChanged(int index)
{
	Editor *editor = qobject_cast<Editor*>(ui->tabWidget->widget(index));

	//if an editor exists, enable editor-specific features (otherwise disable)
	bool enabled = (bool)editor;

	ui->actionFind->setEnabled(enabled);
	ui->actionReplace->setEnabled(enabled);
	ui->actionHighlight->setEnabled(enabled);
	ui->actionGo_to_Line->setEnabled(enabled);
	ui->actionPaste->setEnabled(enabled);
	ui->actionPage_Setup->setEnabled(enabled);
	ui->actionPrint->setEnabled(enabled);
	ui->actionPrint_Preview->setEnabled(enabled);
	ui->actionSelect_All->setEnabled(enabled);
	ui->actionSelect_None->setEnabled(enabled);
	ui->actionIndent->setEnabled(enabled);
	ui->actionUnindent->setEnabled(enabled);
	ui->actionSave->setEnabled(enabled);
	ui->actionSave_As->setEnabled(enabled);
	ui->actionClose_File->setEnabled(enabled);
	ui->actionStatistics->setEnabled(enabled);

	//disable conditional-dependent actions (re-anable below if conditions are met)
	toggleCutCopyButtons(false);
	ui->menuProgramming_Language->setEnabled(false);
	ui->actionLine_Numbers->setEnabled(false);
	ui->actionUndo->setEnabled(false);
	ui->actionRedo->setEnabled(false);

	if (editor)
	{
		toggleCutCopyButtons(editor->textCursor().hasSelection());	//set copy and cut buttons accordingly

		TextEditor *textEditor = qobject_cast<TextEditor*>(editor);

		if (textEditor)
		{
			ui->menuProgramming_Language->setEnabled(true);
			ui->actionLine_Numbers->setEnabled(true);

			switch (textEditor->getProgrammingLanguage())
			{
				case TextEditor::NONE:
					ui->actionNone->setChecked(true);
					break;

				case TextEditor::C_CPLUSPLUS:
					ui->actionC_CPlusPlus->setChecked(true);
					break;

				case TextEditor::PYTHON:
					ui->actionPython->setChecked(true);
					break;

				case TextEditor::SH_BASH:
					ui->actionSh_Bash->setChecked(true);
					break;

				case TextEditor::JAVA:
					ui->actionJava->setChecked(true);
					break;

				case TextEditor::HTML:
					ui->actionHTML->setChecked(true);
					break;

				case TextEditor::JAVASCRIPT:
					ui->actionJavaScript->setChecked(true);
					break;

				case TextEditor::CSHARP:
					ui->actionCSharp->setChecked(true);
					break;

				case TextEditor::FORTRAN:
					ui->actionFortran->setChecked(true);
					break;
			}
		}
	}
}

/**
  * Bring up preferences dialog.
  */
void MainWindow::showPrefs()
{
	prefs->show();
	prefs->raise();
	prefs->setVisible(true);
}

void MainWindow::showAbout()
{
	if (!about)
		about = new About(this);

	about->show();
	about->raise();
	about->setVisible(true);
}

void MainWindow::showStats()
{
	Editor *editor = qobject_cast<Editor*>(ui->tabWidget->currentWidget());

	if (!stats)
		stats = new Statistics(this);

	if (editor)
	{
		stats->show(editor);
		stats->raise();
		stats->setVisible(true);
	}
}

void MainWindow::showManual()
{
	if (!manual)
		manual = new Manual(this);

	manual->show();
	manual->raise();
	manual->setVisible(true);
}

QString MainWindow::getNextTabName()
{
	QString name(TextEditor::DEFAULT_NAME);
	int suffix = 2;
	int count = documentMenuList->actions().count();

	for (int index = 0; index < count; index++)
	{
		if (documentMenuList->actions().at(index)->text() == name)
		{
			name = TextEditor::DEFAULT_NAME +  QString(" ") +
				   QString::number(suffix);

			suffix++;

			// Begin search again, this is needed as tabs can move around.
			index = 0;
		}
	}

	return name;
}

void MainWindow::pageSetup()
{
	QPageSetupDialog(this).exec();
}

/**
  * Print the current tab.
  * Holy hell, I love Qt! Look at it! That's it? That's all I have to do?
  */
void MainWindow::print(QPrinter *printer)
{
	Editor *editor = qobject_cast<Editor *>(ui->tabWidget->currentWidget());

	// No printer given, so lets create one.
	if (!printer)
	{
		QPrintDialog printDialog(this);

		if (printDialog.exec() == QDialog::Accepted)
			printer = printDialog.printer();
	}

	editor->print(printer);
}

void MainWindow::printPreview()
{
	QPrintPreviewDialog *preview = NULL;
	QPrinter printer(QPrinter::HighResolution);

	preview = new QPrintPreviewDialog(&printer, this);

	connect(preview, SIGNAL(paintRequested(QPrinter*)), this, SLOT(print(QPrinter*)));

	preview->exec();
}

void MainWindow::showFind(const bool show)
{
	if (!find)
	{
		find = new Find(qobject_cast<Editor *>(ui->tabWidget->currentWidget()));

		ui->centralwidget->layout()->addWidget(find);
	}

	find->disableReplace();

	ui->actionReplace->setChecked(false);

	if (!show)
		find->hide();

	else
	{
		find->show();

		ui->actionFind->setChecked(true);
	}
}

void MainWindow::showReplace(const bool show)
{
	if (!find)
	{
		find = new Find(qobject_cast<Editor *>(ui->tabWidget->currentWidget()));

		ui->centralwidget->layout()->addWidget(find);
	}

	find->enableReplace();

	ui->actionFind->setChecked(false);

	if (!show)
		find->hide();

	else
	{
		find->show();

		ui->actionReplace->setChecked(true);
	}
}

void MainWindow::chatSend() const
{
	QString msg = ui->lineEdit_chat->text();

	session->getChatManager()->sendMsg(msg);

	ui->lineEdit_chat->clear();
}

void MainWindow::chatReceive(QString msg) const
{
	ui->textBrowser_chat->append(msg);
}

/**
  *	This slot updates the user list given (id,name,color,enabled) properties.
  * The \a enabled if false means the user should be removed.
  */
void MainWindow::updateUserList(int id, QString name, QColor colour, bool enabled)
{
	if (enabled == false)
	{
			//Hide the item using the below (or similar)
	      //  ui->listWidget_users->item(id)->setFlags(Qt::NoItemFlags);
		name+=" - Offline";
	}
	//else
	//{
		//set up the list item
		QPixmap colourMap(16,16);
		colourMap.fill(colour);

		QListWidgetItem *newItem = new QListWidgetItem(QIcon(colourMap), name);

				newItem->setFlags(Qt::ItemIsEnabled);

		if (ui->listWidget_users->count() > id)
		{
			//edit existing user
			QListWidgetItem *oldItem = ui->listWidget_users->item(id);
			ui->listWidget_users->removeItemWidget(oldItem);
			delete oldItem;

			ui->listWidget_users->insertItem(id, newItem);
		}
		else
		{
			//add new user
			ui->listWidget_users->addItem(newItem);
		}
	//}
}

void MainWindow::saveSession()
{
	//Put up file dialogue to choose where to save file
	QString filename = "";
	filename = QFileDialog::getSaveFileName(this, tr("Save Session As"), "./", tr("WeaveEdit Files (*.weave, *.wed)"));

	if(filename!="")    //File was set
	{
	    //Output data of mainwindow
	    QFile file(filename+".weave");  //For testing purposes, the .weave will eventually become the zip that will need to be expanded before processing
	    if(!file.open(QIODevice::WriteOnly))
		return;

	    file.close();

	    //Call next functions
	    session->saveSession(filename);
	}
}

void MainWindow::loadSession(const QString inputFile)
{
    QString filename = "";       //Get path to session to be opened

    if (inputFile.isEmpty())
	filename = QFileDialog::getOpenFileName(this, tr("Open Session"), "./", tr("WeaveEdit Files (*.weave *.wed)"));

    else
	filename = inputFile;

    if(filename!="")    //File was selected
    {
	OpenSession *openDialog = new OpenSession(this);

	if (openDialog->exec() == QDialog::Accepted)
	{
		hideWelcome();

		QString sessionName = openDialog->getSessionName();
		int port = openDialog->getPort();

		/*User usr = session->getUserManager()->getLocalUser();
		usr.setName(openDialog->getUserName());
		usr.setOnlineStatus(true);
		session->getUserManager()->setLocalUser(usr);*/

		ui->actionCreate_Session->setEnabled(false);
		ui->actionJoin_Session->setEnabled(false);
		ui->actionJoin_Session->setVisible(false);
		ui->actionLeave_Session->setVisible(true);
		ui->actionLeave_Session->setEnabled(true);
		ui->dockWidget_users->show();
		ui->dockWidget_chat->show();
		ui->actionChat->setEnabled(true);
		ui->actionUsers->setEnabled(true);
		ui->actionHighlight->setEnabled(true);
		ui->actionSave_Session->setEnabled(true);
		ui->actionOpen_Session->setEnabled(false);

		session->runAsServer(sessionName, port);
	}

	// Strip last filename extension - '.weave'.
	QFileInfo file(filename);

	// Don't worry, Qt uses the '/' on windows too (I guess, for convenience.)
	filename = file.absolutePath() + '/' + file.completeBaseName();

	session->loadSession(filename);             //Begin opening files to load the session

	delete openDialog;
    }
}

/**
  * Allows a user to go to the start of the requrested line.
  */
void MainWindow::showGoToLine()
{
	if (!goToLine)
		goToLine = new GoToLine(this);

	// Get a line number using the max lines in the file and the current block/line.
	if (goToLine->exec(getCurrentEditor()->document()->lineCount(),
			   getCurrentEditor()->textCursor().blockNumber() + 1) == QDialog::Accepted)
	{
		int line = goToLine->getValue();

		QTextCursor cursor = getCurrentEditor()->textCursor();

		cursor.setPosition(0);

		// Move down until requested line found or end of document.
		for (int i = 1; i < line && i < getCurrentEditor()->document()->lineCount(); i++)
			cursor.movePosition(QTextCursor::Down);

		getCurrentEditor()->setTextCursor(cursor);
	}
}

/**
  * Enables cut, copy and delete if the user has a selection in the document
  */
void MainWindow::toggleCutCopyButtons(bool hasSelection)
{
	ui->actionCopy->setEnabled(hasSelection);
	ui->actionCut->setEnabled(hasSelection);
	ui->actionDelete->setEnabled(hasSelection);
}
